import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus, Logger } from '@nestjs/common'
import { Request, Response } from 'express'
import { ValidationError } from 'class-validator'

/**
 * HTTP异常过滤器
 * 统一处理应用中的异常和错误响应
 */
@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger(HttpExceptionFilter.name)

  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp()
    const response = ctx.getResponse<Response>()
    const request = ctx.getRequest<Request>()

    let status: number
    let message: string | string[]
    let error: string
    let details: any = null

    // 处理不同类型的异常
    if (exception instanceof HttpException) {
      status = exception.getStatus()
      const exceptionResponse = exception.getResponse()

      if (typeof exceptionResponse === 'string') {
        message = exceptionResponse
        error = exception.name
      } else if (typeof exceptionResponse === 'object') {
        const response = exceptionResponse as any
        message = response.message || response.error || exception.message
        error = response.error || exception.name
        details = response.details
      }
    } else if (exception instanceof Error && exception.name === 'PrismaClientKnownRequestError') {
      // Prisma数据库错误
      status = HttpStatus.BAD_REQUEST
      error = 'Database Error'
      message = this.handlePrismaError(exception)
    } else if (exception instanceof Error) {
      // 其他错误
      status = HttpStatus.INTERNAL_SERVER_ERROR
      error = 'Internal Server Error'
      message = process.env.NODE_ENV === 'production' ? '服务器内部错误' : exception.message
      details = process.env.NODE_ENV !== 'production' ? exception.stack : undefined
    } else {
      // 未知错误
      status = HttpStatus.INTERNAL_SERVER_ERROR
      error = 'Unknown Error'
      message = '未知错误'
    }

    // 构建错误响应
    const errorResponse = {
      code: status,
      message,
      error,
      timestamp: new Date().toISOString(),
      path: request.url,
      method: request.method,
      ...(details && { details })
    }

    // 记录错误日志
    this.logError(exception, request, errorResponse)

    // 发送错误响应
    response.status(status).json(errorResponse)
  }

  /**
   * 处理Prisma数据库错误
   */
  private handlePrismaError(error: any): string {
    const message = error.message

    // Prisma错误码处理
    if (error.code) {
      switch (error.code) {
        case 'P2002': // unique_violation
          return '数据已存在，请检查唯一性约束'
        case 'P2003': // foreign_key_violation
          return '外键约束违反，相关数据不存在'
        case 'P2011': // null_constraint_violation
          return '必填字段不能为空'
        case 'P2025': // record_not_found
          return '记录不存在'
        case 'P1001': // connection_error
          return '数据库连接失败'
        default:
          return process.env.NODE_ENV === 'production' ? '数据库操作失败' : message
      }
    }

    return process.env.NODE_ENV === 'production' ? '数据库操作失败' : message
  }

  /**
   * 记录错误日志
   */
  private logError(exception: unknown, request: Request, errorResponse: any) {
    const { method, url, ip, headers } = request
    const userAgent = headers['user-agent'] || ''
    const userId = (request as any).user?.id || 'anonymous'

    const logContext = {
      userId,
      method,
      url,
      ip,
      userAgent,
      timestamp: errorResponse.timestamp,
      statusCode: errorResponse.code
    }

    if (errorResponse.code >= 500) {
      // 服务器错误 - 记录为错误级别
      this.logger.error(`${method} ${url} - ${errorResponse.code} - ${errorResponse.message}`, {
        ...logContext,
        error: exception instanceof Error ? exception.stack : exception
      })
    } else if (errorResponse.code >= 400) {
      // 客户端错误 - 记录为警告级别
      this.logger.warn(`${method} ${url} - ${errorResponse.code} - ${errorResponse.message}`, logContext)
    }
  }
}

/**
 * 验证异常过滤器
 * 专门处理数据验证错误
 */
@Catch(ValidationError)
export class ValidationExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger(ValidationExceptionFilter.name)

  catch(exception: ValidationError[], host: ArgumentsHost) {
    const ctx = host.switchToHttp()
    const response = ctx.getResponse<Response>()
    const request = ctx.getRequest<Request>()

    const errors = this.formatValidationErrors(exception)

    const errorResponse = {
      code: HttpStatus.BAD_REQUEST,
      message: '数据验证失败',
      error: 'Validation Error',
      errors,
      timestamp: new Date().toISOString(),
      path: request.url,
      method: request.method
    }

    this.logger.warn(`Validation failed for ${request.method} ${request.url}`, {
      errors,
      userId: (request as any).user?.id || 'anonymous',
      ip: request.ip
    })

    response.status(HttpStatus.BAD_REQUEST).json(errorResponse)
  }

  /**
   * 格式化验证错误
   */
  private formatValidationErrors(errors: ValidationError[]): any[] {
    return errors.map(error => ({
      field: error.property,
      value: error.value,
      constraints: error.constraints,
      children: error.children?.length > 0 ? this.formatValidationErrors(error.children) : undefined
    }))
  }
}

/**
 * 全局异常过滤器
 * 捕获所有未处理的异常
 */
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger(GlobalExceptionFilter.name)

  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp()
    const response = ctx.getResponse<Response>()
    const request = ctx.getRequest<Request>()

    let status = HttpStatus.INTERNAL_SERVER_ERROR
    let message = '服务器内部错误'
    let error = 'Internal Server Error'

    if (exception instanceof HttpException) {
      status = exception.getStatus()
      const exceptionResponse = exception.getResponse()

      if (typeof exceptionResponse === 'string') {
        message = exceptionResponse
      } else if (typeof exceptionResponse === 'object') {
        const response = exceptionResponse as any
        message = response.message || exception.message
        error = response.error || exception.name
      }
    }

    const errorResponse = {
      code: status,
      message,
      error,
      timestamp: new Date().toISOString(),
      path: request.url,
      method: request.method,
      requestId: this.generateRequestId()
    }

    // 记录详细错误信息
    this.logger.error(`Unhandled exception: ${request.method} ${request.url}`, {
      exception: exception instanceof Error ? exception.stack : exception,
      requestId: errorResponse.requestId,
      userId: (request as any).user?.id || 'anonymous',
      ip: request.ip,
      userAgent: request.headers['user-agent'],
      body: request.body,
      query: request.query,
      params: request.params
    })

    response.status(status).json(errorResponse)
  }

  /**
   * 生成请求ID
   */
  private generateRequestId(): string {
    return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
  }
}
